#import "BWWrapList.h"
#import "BWSymbol.h"



@implementation BWWrapList


- (id) initWithFrame: (NSRect) frame
{
    if (self = [super initWithFrame: frame]) {
        cellWidth = cellHeight = 30;
        padWidth = padHeight = 5;
    }

    return (self);

} // initWithFrame


- (void) bind: (NSString *) bindingName
     toObject: (id) observableObject
  withKeyPath: (NSString *) observableKeyPath
      options: (NSDictionary *) options
{
    if ([bindingName isEqualToString: @"value"]) {

        // hold on to the binding info
        valueBinding = observableObject;
        [observableKeyPath release];
        valueKeyPath = [observableKeyPath copy];

        // connect KVO
        [valueBinding addObserver: self
                      forKeyPath: valueKeyPath
                      options: nil
                      context: NULL];

        // new binding, need to redraw
        [self setNeedsDisplay: YES];

    } else if ([bindingName isEqualToString: @"selection"]) {

        selectionBinding = observableObject;
        [selectionKeyPath release];
        selectionKeyPath = [observableKeyPath copy];
        
        // connect KVO
        [selectionBinding addObserver: self
                          forKeyPath: selectionKeyPath
                          options: nil
                          context: NULL];

        // new binding, need to redraw
        [self setNeedsDisplay: YES];

    } else {
        [super bind: bindingName
               toObject: observableObject
               withKeyPath: observableKeyPath
               options: options];
    }

} // bind


// KVO notification.  One of our bindees has had a change, so track it

- (void) observeValueForKeyPath: (NSString *) kp
                       ofObject: (id) object
                         change: (NSDictionary *) change
                        context: (void *) context
{
    // see what bindee thing changed

    if (object == valueBinding) {
        [self setNeedsDisplay: YES];

    } else if (object == selectionBinding) {
        [self setNeedsDisplay: YES];

    } else {
        [super observeValueForKeyPath: kp
               ofObject: object
               change: change
               context: context];
    }

} // observeValueForKeyPath


// query the binding for the array to use for the list

- (id) value
{
    id thing = [valueBinding valueForKeyPath: valueKeyPath];

    if (thing == NSNoSelectionMarker) {
        thing = nil;
    }

    return (thing);

} // value


- (int) valueCount
{
    int count = 0;

    if ([self value]) {
        count = [[[self value] valueForKeyPath: @"@count"] intValue];
    }

    return (count);

} // valueCount


- (id) selection
{
    id thing = [selectionBinding valueForKeyPath: selectionKeyPath];

    if (thing == NSNoSelectionMarker) {
        thing = nil;
    }

    return (thing);

} // selection
    

- (NSRect) cellRectForRow: (int) row  column: (int) column
{
    NSRect rect;

    rect = NSMakeRect ((cellWidth + 2.0 + padWidth*2) * column,
                       (cellHeight + 2.0 + padHeight*2) * row,
                       cellWidth + 2.0 + padWidth*2,
                       cellHeight + 2.0 + padHeight*2);

    // skip over border
    rect.origin.x += 1.0;
    rect.origin.y += 1.0;

    return (rect);

} // cellRectForRow


- (NSRect) cellRectForItem: (int) item
{
    // figure out the row/column

    int row;
    int column;

    row = item / cellsPerRow;
    column = item - (row * cellsPerRow);

    NSRect cellRect;
    cellRect = [self cellRectForRow: row  column: column];

    return (cellRect);

} // cellRectForItem


- (int) mapPointToItem: (NSPoint) point
{
    int item;
    item = -1;

    BOOL found = NO;

    // maximal cheese!
    int row, column;
    for (row = 0; row < rows; row++) {
        for (column = 0; column < cellsPerRow; column++) {
            NSRect cellRect;
            cellRect = [self cellRectForRow: row  column: column];
            if (NSPointInRect(point, cellRect)) {
                found = YES;
                goto done;
            }
        }
    }
done:

    if (found) {
        int itemCount;
        itemCount = [self valueCount];

        item = column + (row * cellsPerRow);
        if (item > itemCount) {
            item = -1;
        }
    }
    
    return (item);

} // mapPointToItem


- (void) drawContents: (id) data  inRect: (NSRect) cellRect
{
    NSBezierPath *path;
    path = [(BWSymbol*)data pathInRect: cellRect];
    
    [path fill];

} // drawContents


- (void) calcRowGeometry
{
    NSRect bounds;
    bounds = [self bounds];

    // skip over border
    NSRect inner;
    inner = NSInsetRect (bounds, 1.0, 1.0);

    int itemCount;
    itemCount = [self valueCount];

    // cells per row:
    cellsPerRow = inner.size.width / (cellWidth + 2.0 + padWidth*2);

    cellsPerRow = MIN (cellsPerRow, itemCount);

    rows = ceilf ((float)itemCount / (float)cellsPerRow);

} // calcRowGeometry


- (void) drawRect: (NSRect) bounds
{
    bounds = [self bounds];

    [[NSColor whiteColor] set];
    [NSBezierPath fillRect: bounds];

    int itemCount;
    itemCount = [self valueCount];

    [self calcRowGeometry];

    int row, column, itemIndex;
    itemIndex = 0;
    for (row = 0; row < rows; row++) {
        for (column = 0; column < cellsPerRow; column++) {
            NSRect cellRect;
            cellRect = [self cellRectForRow: row  column: column];

            id data;
            data = [[self value] objectAtIndex: itemIndex];

            [[NSColor lightGrayColor] set];
            
            [NSBezierPath strokeRect: cellRect];
            [[NSColor blackColor] set];
            
            cellRect = NSInsetRect (cellRect, 1.0 + padWidth, 1.0 + padHeight);

            [self drawContents: data  inRect: cellRect];

            itemIndex++;
            if (itemIndex >= itemCount) {
                goto done;
            }
        }
    }

done:; // ';' because the compiler gets confused with a variable decl after a label

    NSIndexSet *selectedIndexes;
    selectedIndexes = [selectionBinding valueForKey: @"selectionIndexes"];

    int i;
    for (i = 0; i < itemCount; i++) {

        if ([selectedIndexes containsIndex: i]) {
            NSRect cellRect;
            cellRect = [self cellRectForItem: i];
            
            [[NSColor redColor] set];
            [NSBezierPath strokeRect: cellRect];
        }
    }

    [[NSColor blackColor] set];
    [NSBezierPath strokeRect: bounds];

} // drawRect


- (void) reloadData
{
    [self setNeedsDisplay: YES];
} // realodData


- (void) setCellWidth: (int) width  height: (int) height
{
    cellWidth = width;
    cellHeight = height;
    [self setNeedsDisplay: YES];

} // setCellWidth


- (void) setPadWidth: (int) width  height: (int) height
{
    padWidth = width;
    padHeight = height;
    [self setNeedsDisplay: YES];

} // setPadWidth


- (BOOL) isFlipped
{
    return (YES); // we're flipping out!!

} // isFlipped



// track dynamic resize

- (void) setFrame: (NSRect) frame
{
    [super setFrame: frame];

    [self calcRowGeometry];

} // setFrame


- (void) selectItem: (int) index
{
    int itemCount;
    itemCount = [self valueCount];

    NSIndexSet *indexSet;
    if (index < itemCount && index >= 0) {
        indexSet = [NSIndexSet indexSetWithIndex: index];
    } else {
        indexSet = [NSIndexSet indexSet];
    }

    [selectionBinding setValue: indexSet  forKey: @"selectionIndexes"];
    [self setNeedsDisplay: YES];

} // selectItem


- (int) selectedIndex
{
    NSIndexSet *indexSet;
    indexSet = [selectionBinding valueForKey: @"selectionIndexes"];
    
    int index = -1;
    if ([indexSet count] > 0) {
        index = [indexSet firstIndex];
    }

    return (index);

} // selectedIndex


- (void) mouseDown: (NSEvent *) event
{
    NSPoint mouse;
    mouse = [self convertPoint: [event locationInWindow]
                  fromView: nil];
    int item;
    item = [self mapPointToItem: mouse];

    [self selectItem: item];
    
} // mouseDown


- (NSImage *) makeDragImage
{
    NSSize size = NSMakeSize(cellWidth, cellHeight);

    NSImage *image;
    image = [[NSImage alloc] initWithSize: size];
    
    NSRect rect;
    rect.origin = NSZeroPoint;
    rect.size = size;

    [image lockFocus]; {
        [image setFlipped: YES];
        
        int selectedIndex = [self selectedIndex];

        id data = [[self value] objectAtIndex: selectedIndex];

        NSColor *clearGray;
        clearGray = [NSColor colorWithDeviceWhite: 0.333 alpha: 0.5];
        [clearGray set];
        [self drawContents: data  inRect: rect];

    } [image unlockFocus];

    return ([image autorelease]);

} // makeDragImage


- (void) mouseDragged: (NSEvent *) event
{
    int selectedIndex = [self selectedIndex];

    if (selectedIndex >= 0) {

        NSPoint mouse;
        mouse = [self convertPoint: [event locationInWindow]
                      fromView: nil];
        
        NSImage *dragImage = [self makeDragImage];
        
        NSPasteboard *pboard;
        pboard = [NSPasteboard pasteboardWithName: NSDragPboard];

        [pboard declareTypes: 
                    [NSArray arrayWithObjects: 
                                 BWSymbol_PackageNameType,
                             BWSymbol_SymbolIdType, nil]
                owner: self];

        BWSymbol *symbol = [[self value] objectAtIndex: selectedIndex];

        [pboard setPropertyList: [NSNumber numberWithInt: [symbol symbolId]]
                forType: BWSymbol_SymbolIdType];
        [pboard setPropertyList: [symbol packageName]
                forType: BWSymbol_PackageNameType];

        NSPoint offset = mouse;

        offset.x -= cellWidth / 2;
        offset.y += cellHeight / 2;
        
        [self dragImage: dragImage
              at: offset
              offset: NSZeroSize
              event: event
              pasteboard: pboard
              source: self
              slideBack: YES];
    }
    
} // mouseDragged


- (void) mouseUp: (NSEvent *) event
{
} // mouseUp


// --------------------------------------------------
// dragon droppings

- (unsigned int) draggingSourceOperationMaskForLocal: (BOOL) isLocal
{
    unsigned int op;

    if (isLocal) {
        op = NSDragOperationNone;
    } else {
        op = NSDragOperationCopy;
    }

    return (op);

} // draggingSourceOperationMaskForLocal

@end // BWWrapList

